<?php

App::uses('CrudController', 'Controller');
/* 
 * Controlador donde se van a realizar los exámenes en base al stock de preguntas generadas
 */

class ExamsController extends CrudController
{
    const SCALE = 100; //Escala para calificar exámenes
    const QUIT_BY_TIME      = 'time';
    const QUIT_BY_GRADE     = 'qualification';
    const QUIT_BY_MANDATORY = 'mandatory';
    const QUIT_BY_USER      = 'user';
    const DIR_FILES_EXAMS   = 'exams';
    const TMP_FILES         = 'PHP_TEMP';
            
    public function index($wsId='')
    {
        $this->request->allowMethod('get');
        $this->layout = 'exam';
        $appBosch = new stdClass();
        $appBosch->urlCancel = '';
        $appBosch->wsId = isset( $wsId ) ? $wsId : '';
        $appBosch->wsConfig = array(
            "time" => 0
        );        
        if( $appBosch->wsId != '' )
        {
            $this->loadModel('Certification');
            $this->loadModel('CertificationDetail');
            $this->loadModel('Question');
            $this->loadModel('Moe');
            $this->loadModel('Area');
            $this->loadModel('Line');
            $this->loadModel('Workstation');
            $this->loadModel('WorkstationConfig');
            
            $certification = $this->Certification->getForExam( $this->Auth->user("id"), $appBosch->wsId );
            $resWSConfig = $this->WorkstationConfig->getForExam( $appBosch->wsId );
            $wsConfig = $resWSConfig['WorkstationConfig'];
            $wsId = $wsConfig["workstation_id"];
            $res = $this->Workstation->findById($wsId);//Obtener WS   //ILR cambiar a la consulta SQL
            $ws = $res["Workstation"];            
            $resL = $this->Line->findById($ws["line_id"]);//Obtener Line
            $line = $resL["Line"];            
            $resA = $this->Area->findById($line["area_id"]);//Obtener Area
            $area = $resA["Area"];
            $resM = $this->Moe->findById($area["moe_id"]);//Obtener Moe
            $moe = $resM["Moe"];
            
            $newExam = array();
            $newExam['id']              = '';
            $newExam['maxQuestions']    = $wsConfig["questions"];
            $appBosch->wsConfig['countQuestions'] = $wsConfig["questions"];
            $resExam = $this->Exam->getForRestart( $wsConfig['id'], $this->Auth->user('id') );
            
            if( $resExam )
            {
                $restartExam = $resExam['Exam'];
                if( $restartExam['end_time'] == NULL )
                {
                    $appBosch->restartExam = TRUE;
                    $newExam['id']                     = $restartExam['id'];
                    $newExam['questions']              = array();
                    $newExam['countQuestions']         = $restartExam['questions'];

                    $appBosch->wsConfig['countQuestions']        = $restartExam['questions'];
                    $appBosch->wsConfig['confQuestions']         = intval($restartExam['questions']);
                }
                else
                {
                    $appBosch->invalidExam = TRUE;
                }
            }
            else
            {//examen tomado desde el inicio
                $questions = array();
                $hierarchy = array('ws_id'=>$ws["id"],'line_id'=>$line["id"],'area_id'=>$area["id"],'moe_id'=>$moe["id"]);
                $questions = array_merge( $questions, $this->Question->getForExam($hierarchy,$newExam['maxQuestions'])); //primero todas las obligatorias .... selectable = false
                if( count( $questions ) < $newExam['maxQuestions'] )
                {   //obtener las preguntas opcionales
                    $questions = array_merge( $questions, $this->Question->getForExam($hierarchy,$newExam['maxQuestions']-count($questions),FALSE)); //primero todas las obligatorias .... selectable = false
                }

                $newExam['questions']               = $questions;
                $newExam['countQuestions']          = count($questions);
                                
                $appBosch->wsConfig['countQuestions']        = count($questions);
                $appBosch->wsConfig['confQuestions']         = intval($wsConfig["questions"]);
                
                $appBosch->evaluation = new stdClass();
                $appBosch->evaluation->wsQualification = -1;
                $appBosch->evaluation->cdQualification = -1;
                $appBosch->validation = new stdClass();
                $appBosch->validation->wsQualification = -1;
                $appBosch->validation->cdQualification = -1;
                $valWSConf = $this->WorkstationConfig->getEvaluationForExam( $appBosch->wsId );
                if( $valWSConf )
                {
                    $valWSConf = $valWSConf['WorkstationConfig'];
                    $appBosch->validation->wsQualification = intval($valWSConf['minimum_qualification']);
                    $valCD = $this->CertificationDetail->getByCertificationAndWorkstationConfig( $certification['id'], $valWSConf['id'] );
                    if( $valCD )
                    {
                        $valCD = $valCD['CertificationDetail'];
                        $appBosch->validation->cdQualification = intval( $valCD['qualification'] );
                    }
                }                
                $evaWSConf = $this->WorkstationConfig->getValidationForExam( $appBosch->wsId );
                if( $evaWSConf )
                {
                    $evaWSConf = $evaWSConf['WorkstationConfig'];
                    $appBosch->evaluation->wsQualification = intval($evaWSConf['minimum_qualification']);
                    $evaCD = $this->CertificationDetail->getByCertificationAndWorkstationConfig( $certification['id'], $evaWSConf['id'] );
                    if( $evaCD )
                    {
                        $evaCD = $evaCD['CertificationDetail'];
                        $appBosch->evaluation->cdQualification = intval( $evaCD['qualification'] );
                    }
                }
            }//examen tomado desde el inicio
                       
            $newExam['certification_id']        = $certification['id'];            
            $newExam['workstation_config_id']   = $wsConfig['id'];
            $newExam['workstation_id']          = $appBosch->wsId;
            $newExam['name']                    = $ws['description'];
            $newExam['maxQuestions']            = $wsConfig["questions"];
            
            $appBosch->wsConfig['time']                  = intval( $wsConfig["time"] / 60 );            
            $appBosch->wsConfig['minimum_qualification'] = $wsConfig['minimum_qualification'];
            $appBosch->wsConfig['name']                  = $newExam['name'];
            $appBosch->wsCId                             = $wsConfig['id'];
            
            $this->Session->write('exam',$newExam);
            $this->set('title', $newExam['name'] );
        }               
                
        $this->set('appBosch', $appBosch );
    }
    
    public function start($wsCid)
    {
        $this->request->allowMethod('get');
        $this->layout = 'exam';
        
        $appBosch = new stdClass();
        
        $this->loadModel('WorkstationConfig');
        $resConf = $this->WorkstationConfig->findById($wsCid);
        $wsConfig   = $resConf["WorkstationConfig"];
        $newExam    = $this->Session->read('exam');
        $currentDate = new DateTime();
        $startDate  = '';
        if( $newExam['id'] == '' )
        {
            $data = array(//Guardar el examen
                'start_time' => $currentDate->format("Y-m-d H:i:s"),
                'end_time' => NULL,
                'user_id' => $this->Auth->user("id"),
                'workstation_config_id' => $wsCid,
                'minimum_qualification' => $wsConfig['minimum_qualification'],
                'questions' => $wsConfig['questions'],
                'time' => $wsConfig['time']
            );
            $resExam = $this->Exam->insert($data);
            $startDate = $data['start_time'];
        }
        else
        {
            $resExam = $this->Exam->findById($newExam['id']);
            $startDate = $resExam['Exam']['start_time'];
        }
        
        if( $resExam )
        {
            $insertedExam = $resExam["Exam"];            
            if( $newExam['id'] != '' || ( $newExam['id'] == '' && $this->_saveQuestionsToApply( $newExam['questions'], $insertedExam['id'] ) ) )
            {
                $appBosch->rc = "00";
                $appBosch->rm = __("Examen creado");
                $appBosch->maxTime      = $newExam['id'] != '' ? $this->_secondsLeft($insertedExam) : intval($wsConfig["time"]);//seconds
                $appBosch->currDate     = $startDate;
                $currentDate->add( new DateInterval('PT'.$appBosch->maxTime.'S'));
                $appBosch->endDate      = $currentDate;
                $newExam['maxTime']     = $appBosch->maxTime;
                $newExam['end_time']    = $currentDate;
                $newExam['id']          = $insertedExam["id"];
                $newExam['minimum_qualification'] = $wsConfig['minimum_qualification'];
                $this->Session->write('exam',$newExam);
                $this->set('title', $newExam["name"] );
            }
            else
            {
                $appBosch->rc = "01";
                $appBosch->rm = __("Imposible iniciar examen, las preguntas no se pudieron respaldar");
            }
        }
        else 
        {
            $appBosch->rc = "02";
            $appBosch->rm = __("Imposible iniciar el examen");
        }
        
        $this->set( 'appBosch', $appBosch );
    }
    
    private function _saveQuestionsToApply( $questions, $examId )
    {
        $this->loadModel('AppliedQuestion');
        $this->loadModel('AppliedAnswer');
        $this->loadModel('Answer');
        $result = TRUE;
        
        foreach( $questions as $key => $question )
        {
            $dataAQuestion = array(
                'source_id' => $question['source_id'],
                'type' => $question['type'],	
                'description' => $question['description'],
                'selectable' => $question['selectable'],
                'status' => $question['status'],
                'image' => '',
                'video' => '',
                'mandatory' => $question['mandatory'],
                'required' => $question['required'],
                'exam_id' => $examId
            );
            $resIns = $this->AppliedQuestion->insert($dataAQuestion);
            if( isset( $resIns['AppliedQuestion']['id'] ) )
            {
                $appliedQId = $resIns['AppliedQuestion']['id'];
                if( $question['video'] != '' || $question['image'] != '' )
                {
                    $urlQVideo = $this->_backUpFile( $question['video'], $appliedQId );
                    $urlQImage = $this->_backUpFile( $question['image'], $appliedQId );
                    $dataAQuestion = array(
                        'image' => $urlQImage,
                        'video' => $urlQVideo
                    );
                    $resIns = $this->AppliedQuestion->update($appliedQId, $dataAQuestion);
                }
                $answers    = $this->Answer->getForQuestionExam($question['id']);
                foreach( $answers as $keyA => $answer )
                {
                    $dataAAnswer = array(
                        'applied_question_id' => $appliedQId,
                        'description' => $answer['description'],
                        'is_ok' => $answer['is_ok'],
                        'image' => '',
                        'status' => $answer['status']
                    );
                    $resInsAns = $this->AppliedAnswer->insert($dataAAnswer);
                    if( !isset($resInsAns['AppliedAnswer']['id']) )
                    {
                        $result = FALSE;
                        break;
                    }
                    else if( $answer['image'] != '' )
                    {
                        $urlAImage = $this->_backUpFile( $answer['image'], $resInsAns['AppliedAnswer']['id'] );
                        $dataAAnswer = array(
                            'image' => $urlAImage
                        );
                        $this->AppliedAnswer->update($resInsAns['AppliedAnswer']['id'], $dataAAnswer);
                    }
                }//for answers
            }
            else
            {
                $result = FALSE;
                break;
            }
        }//for
        return $result;
    }

    private function _backUpFile($path, $name)
    {
        $newPath = "";
        if ($path != "") {
            $ext = pathinfo($path, PATHINFO_EXTENSION);//obtener extensión del archivo
            $dir = TMP . self::DIR_FILES_EXAMS;
            if (!file_exists($dir) && !is_dir($dir))//si el directorio de respaldo de exámenes no existe
            {
                mkdir($dir, 0755);
            }
            if (file_exists($dir) && is_dir($dir)) {
                $destiny = $dir . DS . $name . '.' . $ext;//creart la nueva ruta
                if (copy($path, $destiny))//intentar copiar el archivo a la nueva ruta
                {
                    $newPath = Router::url('/', true) . self::TMP_FILES . DS . self::DIR_FILES_EXAMS . DS . $name . '.' . $ext;
                }
            }
        }

        return $newPath;
    }
    
    private function _secondsLeft($insertedExam)
    {
        $seconds = intval( $insertedExam['time'] ) - ( strtotime($insertedExam['upd_time']) - strtotime($insertedExam['start_time']) );
        return $seconds;
    }
    
    /**
     * Obtiene la siguiente pregunta validando las opciones necesarias
     * - $currQuestion
     * Valida que la petición este dentro del tiempo
     * $skiQuestion = $id
     * Si trae respuesta
     * - - Guarda la respuesta de la pregunta actual
     * - - Quitarla de la lista de questions
     * - - Iniciar
     * Buscar la siguiente pregunta de question mientras sea diferente de skipQuestion
     */
    public function nextQuestion()
    {
        $this->request->allowMethod('get');
        $exam           = $this->Session->read("exam");
        $respQuestion   = array();

        $this->loadModel('AppliedAnswer');
        $this->loadModel('AnsweredQuestion');

        $mandatory_correct = 1;
        if( isset($this->request->query['id']) && isset($this->request->query['answers']) )//si hay respuesta se guarda
        {
            $result = $this->_saveAnsweredQuestion($this->request->query['id'],$this->request->query['answers']);
            $mandatory_correct = $result['mandatory_correct'];
            $exam       = $this->Session->read("exam");
        }
        $nextQuestion = array();
        if( $mandatory_correct )
        {
            $currDate = new DateTime();
            if( $currDate < $exam['end_time'] )
            { ///validación por tiempo
                $resume = $this->_getResume($exam['id']);
                $answered = $resume['total'];
                $right    = $resume['right'];
                $minimum_q = $exam['minimum_qualification'];
                $max_errors =  intval($exam['maxQuestions'] ) - intval( ( floatval($minimum_q) * intval($exam['maxQuestions'] ) ) / self::SCALE );
                if( ($answered-$right) > $max_errors  ) //if errores <= max_erros permitidos
                {//terminar examen
                    $respQuestion['rc'] = "02";
                    $respQuestion['rm'] = __('Examen Finalizado, no se alcanzó la calificación mínima.');
                }
                else
                {
                    $currQuestionId = isset($this->request->query['id']) ? $this->request->query['id'] : 0;
                    $nextQuestion   = $this->_getRandomQuestion( $currQuestionId, isset($this->request->query['skip']), $exam['id'] );
                    if( $nextQuestion != NULL && $nextQuestion['id'] != 0 ) // si termino el examen, ya no hay preguntas disponibles
                    {
                        $nextQuestion['mandatory']  = intval($nextQuestion['mandatory']);
                        $nextQuestion['answers']    = $this->AppliedAnswer->getForQuestionExamView($nextQuestion['id']);

                        $respQuestion['rc'] = '00';
                        $respQuestion['rm'] = __('Siguiente pregunta.');
                    }
                    else
                    {
                        $respQuestion['rc'] = '03';
                        $respQuestion['rm'] = __('Examen Finalizado.'); //se acabaron las pregutnas
                    }
                }
            }
            else
            {
                $respQuestion['rc'] = '01';
                $respQuestion['rm'] = __('Examen terminado, se terminó el tiempo.');
            }
        } else
        {
            $respQuestion['rc'] = '04';
            $respQuestion['rm'] = __('Examen terminado, pregunta obligatoria contestada incorrectamente.');
        }
        //actualizar tiempo del examen
        $currentDate = new DateTime();
        $data = array( 'upd_time' => $currentDate->format("Y-m-d H:i:s") );
        $this->Exam->update($exam['id'],$data);

        $respQuestion['question']   = $nextQuestion;
        $respQuestion['totalQs']    = $exam['countQuestions'];
        $respQuestion['count']      = intval($exam['countQuestions']) - count( $this->AnsweredQuestion->getAnsweredQuestionIds($exam['id']));
        $this->set(array('question' => $respQuestion, '_serialize' => 'question'));
        $this->viewClass = 'Json';
    }

    /**
     * Guarda la respuesta contestada y elimina la question_id de las pendientes para el examen
     */
    private function _saveAnsweredQuestion( $questionId, $answers )
    {
        $result = array(
            'mandatory_correct' => 1,
            'rc' => "00"
        );
        $this->loadModel('AppliedAnswer');
        $this->loadModel('AppliedQuestion');
        $this->loadModel('Question');
        //obtener los ids de las answers correctas
        foreach( $answers as $answerId )
        {
            $data = array(
                'applied_question_id' => $questionId,
                'applied_answer_id' => $answerId
            );
            $this->AnsweredQuestion->insert($data);
        }//
        $resAppliedQuestion = $this->AppliedQuestion->findById($questionId);//obtener question
        $appliedQuestion    = $resAppliedQuestion['AppliedQuestion']; //determinar si es mandatory_correct
        if( intval($appliedQuestion['mandatory'] ) == Question::MANDATORY )
        {
            $answersOK = $this->AppliedAnswer->getAnswersOk( $questionId );
            $idsOK = array();
            foreach( $answersOK as $answerOK )
            {
                array_push( $idsOK, $answerOK['id'] );
            }
            $result = array(
                'mandatory_correct' => $this->_isAnswerAdmissibleCorrect( $idsOK, $answers, intval($appliedQuestion['admissible_answers']) ),
                'rc' => "01"
            );
        }

        return $result;
    }

    /**
     * Arreglo con las respuestas correctas
     * Arreglo con las respuestas que el usuario contestó
     * $admissible indica el número mínimo de respuestas para considerar la pregunta correcta
     */
    private function _isAnswerAdmissibleCorrect($okIds, $answeredIds, $admissible)
    {
        $right = 0;
        foreach ($answeredIds as $answer) {
            if (in_array($answer, $okIds)) {
                $right++;
            }
        }
        return ($right >= $admissible);
    }

    private function _getResume($examId)
    {
        $this->loadModel('AnsweredQuestion');
        $this->loadModel('AppliedQuestion');
        $this->loadModel('AppliedAnswer');
        $resume = $this->AnsweredQuestion->getResume($examId);
        $right = 0;
        //obtener las unique applied_questions contestadas
        $ids = $this->AnsweredQuestion->getAnsweredQuestionIds($examId);
        foreach ($ids as $idQuestion) {
            $answersOk = $this->AppliedAnswer->getIdsOkBy($idQuestion);
            $answersUser = $this->AnsweredQuestion->getIdAnswersBy($idQuestion);
            $resAppliedQuestion = $this->AppliedQuestion->findById($idQuestion);//obtener question
            if ($this->_isAnswerAdmissibleCorrect($answersOk, $answersUser, $resAppliedQuestion['AppliedQuestion']['admissible_answers'])) {
                $right++;
            }
        }
        $resume['right'] = $right;
        return $resume;
    }

    /**
     * Regresa una pregunta aleatoria diferente a la de $id
     */
    private function _getRandomQuestion($currQuestionId, $skip, $examId)
    {
        $this->loadModel('AppliedQuestion');
        $this->loadModel('AnsweredQuestion');
        if ($skip) {//marcar
            $this->AppliedQuestion->skipQuestion($currQuestionId);
        }
        return $this->AppliedQuestion->nextQuestion($examId, $this->AnsweredQuestion->getAnsweredQuestionIds($examId));
    }
    
    public function resume($quit)
    {
        $this->request->allowMethod('get');
        $appBosch = new stdClass();
        $exam = $this->Session->read('exam');
        $appBosch->total        = 0;
        $appBosch->answered     = 0;
        $appBosch->right        = 0;
        $appBosch->minimum_q    = 0;
        $appBosch->grade        = 0;
        if( $exam )
        {
            $currentDate = new DateTime();
            $data = array( "end_time" => $currentDate->format("Y-m-d H:i:s") );
            $this->Exam->update($exam['id'],$data);

            $this->loadModel('CertificationDetail');
            $this->loadModel('WorkstationConfig');
            $this->loadModel('AnsweredQuestion');

            $resConf            = $this->WorkstationConfig->findById($exam['workstation_config_id']);
            $wsConfig           = $resConf["WorkstationConfig"];
            $appBosch->total    = $wsConfig["questions"];
            $resume             = $this->_getResume($exam['id']);
            $appBosch->answered = $resume['total'];
            $appBosch->right    = $resume['right'];
            $appBosch->minimum_q = $exam['minimum_qualification'];
            $appBosch->grade    = $appBosch->right*self::SCALE / $appBosch->total;

            $dataCer = array(
                'workstation_config_id' => $exam['workstation_config_id'],
                'certification_id' => $exam['certification_id'],
                'qualification' => $appBosch->grade,
                'certification_date' => $currentDate->format("Y-m-d H:i:s")
            );
            $this->CertificationDetail->insert($dataCer);
            $this->Session->delete('exam');
        }
        $appBosch->quitBy = '00';
        if( $quit == self::QUIT_BY_TIME )
        {
            $appBosch->quitBy = '01';
        }
        else if( $quit == self::QUIT_BY_MANDATORY )
        {
            $appBosch->quitBy = '03';
        }
        else if( $quit == self::QUIT_BY_GRADE )
        {
            $appBosch->quitBy = '04';
        }
        else if( $quit == self::QUIT_BY_USER )
        {
            $appBosch->quitBy = '02';
        }
        $this->set( 'title', __('Examen Finalizado') );
        $this->set('appBosch',$appBosch);
    }
    
    protected function c($model) {
        return false;
    }

    protected function getRecords($argList) {
        return array();
    }

    protected function u($model) {
        return false;
    }
}
